package raytracing

import (
	"image/color"
	"math"
)

type Vec3 struct {
	X float64
	Y float64
	Z float64
}

func V(x, y, z float64) Vec3 {
	return Vec3{
		X: x,
		Y: y,
		Z: z,
	}
}

func RandomVec3() Vec3 {
	return Vec3{
		X: Random(),
		Y: Random(),
		Z: Random(),
	}
}

func RandomBetweenVec3(min float64, max float64) Vec3 {
	return Vec3{
		X: RandomBetween(min, max),
		Y: RandomBetween(min, max),
		Z: RandomBetween(min, max),
	}
}

func RandomInUnitSphereVec3() Vec3 {
	for {
		p := RandomBetweenVec3(-1, 1)
		if p.LengthSquared() >= 1 {
			continue
		}
		return p
	}
}

func RandomUnitVector() Vec3 {
	return RandomInUnitSphereVec3().UnitVector()
}

func RandomVectorInHemiphere(normal Vec3) Vec3 {
	inUnitSphere := RandomInUnitSphereVec3()
	if inUnitSphere.Dot(normal) > 0 {
		return inUnitSphere
	} else {
		return inUnitSphere.Negate()
	}
}

func (v *Vec3) NearZero() bool {
	s := 1e-8

	return math.Abs(v.X) < s && math.Abs(v.Y) < s && math.Abs(v.Z) < s
}

func (v Vec3) Add(u Vec3) (r Vec3) {
	r.X = v.X + u.X
	r.Y = v.Y + u.Y
	r.Z = v.Z + u.Z

	return
}

func (v Vec3) AddScalar(u float64) (r Vec3) {
	r.X = v.X + u
	r.Y = v.Y + u
	r.Z = v.Z + u

	return
}

func (v Vec3) Sub(u Vec3) (r Vec3) {
	r.X = v.X - u.X
	r.Y = v.Y - u.Y
	r.Z = v.Z - u.Z

	return
}

func (v Vec3) SubScalar(u float64) (r Vec3) {
	r.X = v.X - u
	r.Y = v.Y - u
	r.Z = v.Z - u

	return
}

func (v Vec3) Mul(u Vec3) (r Vec3) {
	r.X = v.X * u.X
	r.Y = v.Y * u.Y
	r.Z = v.Z * u.Z

	return
}

func (v Vec3) MulScalar(u float64) (r Vec3) {
	r.X = v.X * u
	r.Y = v.Y * u
	r.Z = v.Z * u

	return
}

func (v Vec3) Div(u Vec3) (r Vec3) {
	r.X = v.X / u.X
	r.Y = v.Y / u.Y
	r.Z = v.Z / u.Z

	return
}

func (v Vec3) DivScalar(u float64) (r Vec3) {
	r.X = v.X / u
	r.Y = v.Y / u
	r.Z = v.Z / u

	return
}

func (v Vec3) Negate() (r Vec3) {
	r.X = -r.X
	r.Y = -r.Y
	r.Z = -r.Z

	return
}

func (v Vec3) Length() float64 {
	return math.Sqrt(v.LengthSquared())
}

func (v Vec3) LengthSquared() float64 {
	return v.X*v.X + v.Y*v.Y + v.Z*v.Z
}

func (v Vec3) Dot(u Vec3) float64 {
	return v.X*u.X + v.Y*u.Y + v.Z*u.Z
}

func (u Vec3) Cross(v Vec3) Vec3 {
	return Vec3{
		u.Y*v.Z - u.Z*v.Y,
		u.Z*v.X - u.X*v.Z,
		u.X*v.Y - u.Y*v.X,
	}
}

func (v Vec3) UnitVector() Vec3 {
	return v.DivScalar(v.Length())
}

func (v Vec3) ToColor(samplesPerPixel int) color.Color {
	scale := 1.0 / float64(samplesPerPixel)
	r := math.Sqrt(scale * v.X)
	g := math.Sqrt(scale * v.Y)
	b := math.Sqrt(scale * v.Z)

	ir := uint8(Clamp(r, 0, 0.999) * 255.99)
	ig := uint8(Clamp(g, 0, 0.999) * 255.99)
	ib := uint8(Clamp(b, 0, 0.999) * 255.99)

	color := color.RGBA{ir, ig, ib, 1}

	return color
}
